package net.qiqbframework.modelling.command;


import net.qiqbframework.config.ConfigurationException;
import net.qiqbframework.messaging.annotation.ClasspathParameterResolverFactory;
import net.qiqbframework.messaging.annotation.HandlerParameterResolverFactory;
import net.qiqbframework.messaging.annotation.MessageHandlingMemberFactory;
import net.qiqbframework.modelling.domain.AggregateModel;
import net.qiqbframework.modelling.domain.AnnotatedAggregateModelFactory;
import net.qiqbframework.modelling.domain.CreationPolicyAggregateFactory;
import net.qiqbframework.modelling.repository.AggregateRepository;
import net.qiqbframework.serialization.Serializer;
import org.javers.core.JaversBuilder;

import static net.qiqbframework.common.BuilderUtils.assertNonNull;

public class AggregateAnnotationCommandHandlerBuilder<T> {

    protected AggregateRepository<T> repository;
    protected CommandTargetResolver commandTargetResolver = AnnotationCommandTargetResolver.builder().build();
    protected Class<T> aggregateType;
    protected HandlerParameterResolverFactory parameterResolverFactory;
    protected MessageHandlingMemberFactory handlerDefinition;
    protected AggregateModel<T> aggregateModel;
    protected CreationPolicyAggregateFactory<T> creationPolicyAggregateFactory;

    private final MultiAggregateHandlingMonitor aggregateHandlingMonitor = new MultiAggregateHandlingMonitor();

    private Serializer aggregateRootSerializer;
    private JaversBuilder javersBuilder;

    public static <T> AggregateAnnotationCommandHandlerBuilder<T> builder() {
        return new AggregateAnnotationCommandHandlerBuilder<>();
    }

    public AggregateAnnotationCommandHandlerBuilder<T> repository(AggregateRepository<T> repository) {
        assertNonNull(repository, "Repository may not be null");
        this.repository = repository;
        return this;
    }

    public AggregateAnnotationCommandHandlerBuilder<T> javersBuilder(JaversBuilder javersBuilder) {
        assertNonNull(javersBuilder, "javersBuilder may not be null");
        this.javersBuilder = javersBuilder;
        return this;
    }

    public AggregateAnnotationCommandHandlerBuilder<T> aggregateRootSerializer(Serializer aggregateRootSerializer) {
        assertNonNull(aggregateRootSerializer, "aggregateRootSerializer may not be null");
        this.aggregateRootSerializer = aggregateRootSerializer;
        return this;
    }

    public AggregateAnnotationCommandHandlerBuilder<T> addAggregateHandlingMonitor(AggregateHandlingMonitor aggregateHandlingMonitor) {

        this.aggregateHandlingMonitor.addMessageMonitor(aggregateHandlingMonitor);
        return this;
    }

    public AggregateAnnotationCommandHandlerBuilder<T> commandTargetResolver(CommandTargetResolver commandTargetResolver) {
        assertNonNull(commandTargetResolver, "CommandTargetResolver may not be null");
        this.commandTargetResolver = commandTargetResolver;
        return this;
    }

    public AggregateAnnotationCommandHandlerBuilder<T> creationPolicyAggregateFactory(CreationPolicyAggregateFactory<T> creationPolicyAggregateFactory) {
        assertNonNull(creationPolicyAggregateFactory, "CreationPolicyAggregateFactory may not be null");
        this.creationPolicyAggregateFactory = creationPolicyAggregateFactory;
        return this;
    }


    public AggregateAnnotationCommandHandlerBuilder<T> aggregateType(Class<T> aggregateType) {
        assertNonNull(aggregateType, "The aggregateType may not be null");
        this.aggregateType = aggregateType;
        return this;
    }


    public AggregateAnnotationCommandHandlerBuilder<T> parameterResolverFactory(HandlerParameterResolverFactory parameterResolverFactory) {
        assertNonNull(parameterResolverFactory, "ParameterResolverFactory may not be null");
        this.parameterResolverFactory = parameterResolverFactory;
        return this;
    }


    public AggregateAnnotationCommandHandlerBuilder<T> handlerDefinition(MessageHandlingMemberFactory handlerDefinition) {
        assertNonNull(handlerDefinition, "HandlerDefinition may not be null");
        this.handlerDefinition = handlerDefinition;
        return this;
    }

    public AggregateAnnotationCommandHandlerBuilder<T> aggregateModel(AggregateModel<T> aggregateModel) {
        assertNonNull(aggregateModel, "AggregateModel may not be null");
        this.aggregateModel = aggregateModel;
        return this;
    }

    public AggregateHandlingMonitor aggregateHandlingMonitor() {

        return aggregateHandlingMonitor;
    }

    public Serializer aggregateRootSerializer() {
        return aggregateRootSerializer;
    }

    protected AggregateModel<T> buildAggregateModel() {
        if (aggregateModel == null) {
            return inspectAggregateModel();
        } else {
            return aggregateModel;
        }
    }

    private AggregateModel<T> inspectAggregateModel() {
        if (parameterResolverFactory == null) {
            parameterResolverFactory = ClasspathParameterResolverFactory.forClass(aggregateType);
        }

        return handlerDefinition == null ? AnnotatedAggregateModelFactory.inspectAggregate(aggregateType, parameterResolverFactory,javersBuilder) : AnnotatedAggregateModelFactory.inspectAggregate(aggregateType, parameterResolverFactory, handlerDefinition,javersBuilder);
    }

    public AggregateAnnotationCommandHandler<T> build() {
        return new AggregateAnnotationCommandHandler<>(this);
    }


    protected void validate() throws ConfigurationException {
        // assertNonNull(repository, "The Repository is a hard requirement and should be provided");
        if (aggregateModel == null) {
            assertNonNull(aggregateType, "No AggregateModel is set, whilst either it or the aggregateType is a hard requirement");
            return;
        }
        assertNonNull(aggregateModel, "No aggregateType is set, whilst either it or the AggregateModel is a hard requirement");
    }

}
